/**
 * @file geminai_sdklink_connect.c
 * @brief 根据后台规则生成获取设备密钥，连接阿里云客户端
 * @author zl
 */
#include "geminai_sdklink_connect.h"
#include "esp_timer.h"

#define OTA_WAIT_TIME 600000 // 10分钟
// 定义定时器句柄
TimerHandle_t ota_reset_timer;
#define TAG   "MQTT_LINK"


uint8_t g_mqtt_process_thread_running = 0;
uint8_t g_mqtt_recv_thread_running = 0;
void *mqtt_handle = NULL;
void *ota_handle = NULL;
void *g_dl_handle = NULL;

// 自定义的内存分配函数，设置元数据
MemoryBlock allocateMemory(size_t size)
{
    MemoryBlock block;
    block.data = calloc(1, size);  // 使用 calloc 进行初始化，初始化为 0
    if (block.data == NULL) {
        GEMINAI_LOGD(TAG, "%s: CALLOC ERROR", __func__);
        block.size = 0;
        return block;
    }
    block.size = size;
    return block;
}

//获取随机数
int generateRandomString(MemoryBlock randBlock)
{
    if(randBlock.data == NULL ||randBlock.size == 0) {
        GEMINAI_LOGD(TAG, "GenerateRandomString INPUT ERROR");
        return -1; 
    }
    const char charset[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    int charsetSize = sizeof(charset) - 1;

    srand(time(NULL));

    for (int i = 0; i < randBlock.size-1; i++) {
        sprintf(randBlock.data + i, "%c", charset[rand() % charsetSize]);
    }

    return 0;
}

//sha265_64加密
int sha256_64_encrypt(const char *src, MemoryBlock encryptBlock)
{
    if (src == NULL || encryptBlock.data == NULL) {
        GEMINAI_LOGD(TAG, "sha256_64_encrypt INPUT ERROR");
        return -1;
    }

    // 检查传入的内存块大小是否足够
    if (encryptBlock.size < MBEDTLS_SHA256_64_SIZE) {
        GEMINAI_LOGD(TAG, "Sha256_64 EncryptBlock Size TOO SMALL");
        return -1;
    }

    mbedtls_sha256_context sha256_ctx;

    unsigned char decrypt[32];

    mbedtls_sha256_init(&sha256_ctx);
    mbedtls_sha256_starts(&sha256_ctx, 0); // 0表示传sha256 ， 1 表示传SHA-244
    mbedtls_sha256_update(&sha256_ctx, (unsigned char *)src, strlen((char *)src));
    mbedtls_sha256_finish(&sha256_ctx, decrypt);

    for (int i = 0; i < 32; i++) {
        sprintf(encryptBlock.data + (i * 2), "%02x", decrypt[i]);
    }

    mbedtls_sha256_free(&sha256_ctx);
    return 0;
}

//md5_32加密
int md5_32_encrypt(const char *src, MemoryBlock encryptBlock)
{
    if (src == NULL || encryptBlock.data == NULL) {
        GEMINAI_LOGD(TAG, "md5_32_encrypt INPUT ERROR");
        return -1;
    }

    // 检查传入的内存块大小是否足够
    if (encryptBlock.size < MBEDTLS_MD5_32_SIZE) {
        GEMINAI_LOGD(TAG, "md5_32 EncryptBlock Size TOO SMALL");
        return -1;
    }

    mbedtls_md5_context md5_ctx;

    unsigned char decrypt[16];

    mbedtls_md5_init(&md5_ctx);
    mbedtls_md5_starts(&md5_ctx);
    mbedtls_md5_update(&md5_ctx, (unsigned char*)src, strlen((char *)src));
    mbedtls_md5_finish(&md5_ctx, decrypt);

    for (int i = 0; i < 16; i++) {
        sprintf(encryptBlock.data + (i * 2), "%02x", decrypt[i]);
    }

    mbedtls_md5_free(&md5_ctx);
    return 0;
}

//http请求回调函数
esp_err_t _http_event_handler(esp_http_client_event_t *evt)
{
    static char *output_buffer;  // Buffer to store response of http request from event handler
    static int output_len;       // Stores number of bytes read
    switch(evt->event_id) {
        case HTTP_EVENT_ERROR:
            GEMINAI_LOGD(TAG, "HTTP_EVENT_ERROR");
            break;
        case HTTP_EVENT_ON_CONNECTED:
            GEMINAI_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
            break;
        case HTTP_EVENT_HEADER_SENT:
            GEMINAI_LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
            break;
        case HTTP_EVENT_ON_HEADER:
            GEMINAI_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
            break;
        case HTTP_EVENT_ON_DATA:
            GEMINAI_LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
            // Clean the buffer in case of a new request
            if (output_len == 0 && evt->user_data) {
                // we are just starting to copy the output data into the use
                memset(evt->user_data, 0, MAX_HTTP_OUTPUT_BUFFER);
            }
            /*
             *  Check for chunked encoding is added as the URL for chunked encoding used in this example returns binary data.
             *  However, event handler can also be used in case chunked encoding is used.
             */
            if (!esp_http_client_is_chunked_response(evt->client)) 
            {
                // If user_data buffer is configured, copy the response into the buffer
                int copy_len = 0;
                if (evt->user_data) {
                    // The last byte in evt->user_data is kept for the NULL character in case of out-of-bound access.
                    copy_len = MIN(evt->data_len, (MAX_HTTP_OUTPUT_BUFFER - output_len));
                    if (copy_len) {
                        memcpy(evt->user_data + output_len, evt->data, copy_len);
                    }
                } else {
                    int content_len = esp_http_client_get_content_length(evt->client);
                    if (output_buffer == NULL) {
                        // We initialize output_buffer with 0 because it is used by strlen() and similar functions therefore should be null terminated.
                        output_buffer = (char *) calloc(content_len + 1, sizeof(char));
                        output_len = 0;
                        if (output_buffer == NULL) {
                            GEMINAI_LOGE(TAG, "Failed to allocate memory for output buffer");
                            return ESP_FAIL;
                        }
                    }
                    copy_len = MIN(evt->data_len, (content_len - output_len));
                    if (copy_len) {
                        memcpy(output_buffer + output_len, evt->data, copy_len);
                    }
                }
                output_len += copy_len;
            }
            else
            {
                int copy_len = 0;
                if (evt->user_data) {
                    // The last byte in evt->user_data is kept for the NULL character in case of out-of-bound access.
                    copy_len = MIN(evt->data_len, (MAX_HTTP_OUTPUT_BUFFER - output_len));
                    if (copy_len) {
                        memcpy(evt->user_data + output_len, evt->data, copy_len);
                    }
                }
                output_len += copy_len;
            }
            break;
        case HTTP_EVENT_ON_FINISH:
            GEMINAI_LOGD(TAG, "HTTP_EVENT_ON_FINISH");
            if (output_buffer != NULL) {
                // Response is accumulated in output_buffer. Uncomment the below line to print the accumulated response
                // ESP_LOG_BUFFER_HEX(TAG, output_buffer, output_len);
                free(output_buffer);
                output_buffer = NULL;
            }
            output_len = 0;
            break;
        case HTTP_EVENT_DISCONNECTED:
            GEMINAI_LOGI(TAG, "HTTP_EVENT_DISCONNECTED");
            int mbedtls_err = 0;
            esp_err_t err = esp_tls_get_and_clear_last_error((esp_tls_error_handle_t)evt->data, &mbedtls_err, NULL);
            if (err != 0) {
                GEMINAI_LOGI(TAG, "Last esp error code: 0x%x", err);
                GEMINAI_LOGI(TAG, "Last mbedtls failure: 0x%x", mbedtls_err);
            }
            if (output_buffer != NULL) {
                free(output_buffer);
                output_buffer = NULL;
            }
            output_len = 0;
            break;
        case HTTP_EVENT_REDIRECT:
            GEMINAI_LOGD(TAG, "HTTP_EVENT_REDIRECT");
            esp_http_client_set_header(evt->client, "From", "user@example.com");
            esp_http_client_set_header(evt->client, "Accept", "text/html");
            esp_http_client_set_redirection(evt->client);
            break;
    }
    return ESP_OK;
}

//url获取设备密钥
void http_rest_with_url(const char *s_url, const char *secret, const char *name, char *d_secret,  char *asset_id,  char *url , char *product_key)
{
    char *signature = calloc(1,256);
    char *local_response_buffer = calloc(1,MAX_HTTP_OUTPUT_BUFFER);
    MemoryBlock md5Encrypt = allocateMemory(MBEDTLS_MD5_32_SIZE);
    MemoryBlock sha256Encrypt = allocateMemory(MBEDTLS_SHA256_64_SIZE);
    MemoryBlock randString = allocateMemory(17);
    // 调用md5加密函数
    if (md5_32_encrypt(name, md5Encrypt) != 0) {
        return;
    }
    //生成随机数
    if (generateRandomString(randString) != 0) {
        return;
    }
    time_t tTime;
    tTime = time(NULL);

    //生成signature
    if (generateRandomString(randString) != 0) {
        return;
    }

    sprintf(signature, "device_name=%s&nonce=%s&secret=%s&timestamp=%lld", (char*)md5Encrypt.data, (char*)randString.data, secret, tTime);

    if (sha256_64_encrypt(signature, sha256Encrypt) != 0) {
        return;
    }
    sprintf(signature, "%s?device_name=%s&nonce=%s&timestamp=%lld&signature=%s",
                        s_url, name, (char*)randString.data, tTime, (char*)sha256Encrypt.data);
    GEMINAI_LOGI(TAG, "get url = %s", signature);

    // 释放内存
    if(sha256Encrypt.data)  { free(sha256Encrypt.data); sha256Encrypt.data = NULL;}
    if(md5Encrypt.data)     { free(md5Encrypt.data);    md5Encrypt.data = NULL;}
    if(randString.data)     { free(randString.data);    randString.data = NULL;}
         
    esp_http_client_config_t config = {
        .url = signature,
        .method = HTTP_METHOD_GET,
        .event_handler = _http_event_handler,
        .user_data = local_response_buffer,        // Pass address of local buffer to get response
        .disable_auto_redirect = true,
    };
    esp_http_client_handle_t client = esp_http_client_init(&config);
    // GET
    esp_http_client_perform(client);

    cJSON *pJsonRoot = cJSON_Parse(local_response_buffer);
    if(pJsonRoot)
    {
        cJSON *pJsonData = cJSON_GetObjectItem(pJsonRoot, "data"); 
        if(pJsonData)
        {
            cJSON *pJsonDeviceSecret = cJSON_GetObjectItem(pJsonData, "device_secret");
            if(pJsonDeviceSecret)
            {
                strcpy(d_secret, pJsonDeviceSecret->valuestring);
            }
            cJSON *pJsonDeviceSecret1 = cJSON_GetObjectItem(pJsonData, "asset_id");
            if(pJsonDeviceSecret1)
            {
                strcpy(asset_id, pJsonDeviceSecret1->valuestring);
            }            
            cJSON *pJsonDeviceSecret2 = cJSON_GetObjectItem(pJsonData, "endpoint");
            if(pJsonDeviceSecret2)
            {
                strcpy(url, pJsonDeviceSecret2->valuestring);
            }       
            cJSON *pJsonDeviceSecret3 = cJSON_GetObjectItem(pJsonData, "product_key");
            if(pJsonDeviceSecret3)
            {
                strcpy(product_key, pJsonDeviceSecret3->valuestring);
            }                                
        }
    }
    esp_http_client_cleanup(client);
    if(signature)               {free(signature);               signature = NULL;}
    if(local_response_buffer)   {free(local_response_buffer);   local_response_buffer = NULL;}
}

////////////////////////////////////////////////////////////////////////


/* TODO: 如果要关闭日志, 就把这个函数实现为空, 如果要减少日志, 可根据code选择不打印
 *
 * 例如: [1577589489.033][LK-0317] mqtt_basic_demo&a13FN5TplKq
 *
 * 上面这条日志的code就是0317(十六进制), code值的定义见core/aiot_state_api.h
 *
 */

/* 日志回调函数, SDK的日志会从这里输出 */
int32_t demo_state_logcb(int32_t code, char *message)
{
    //printf("%s", message);
    return 0;
}

// 上报版本号
void report_version(void)
{
    char* cur_version = "1.0.2";
    /* 演示MQTT连接建立起来之后, 就可以上报当前设备的版本号了 */
    int32_t res = aiot_ota_report_version(ota_handle, cur_version);
    if (res < STATE_SUCCESS) {
        printf("report version failed, code is -0x%04lX\r\n", -res);
    }
}

/* MQTT事件回调函数, 当网络连接/重连/断开时被触发, 事件定义见core/aiot_mqtt_api.h */
void demo_mqtt_event_handler(void *handle, const aiot_mqtt_event_t *event, void *userdata)
{
    switch (event->type) {
        /* SDK因为用户调用了aiot_mqtt_connect()接口, 与mqtt服务器建立连接已成功 */
        case AIOT_MQTTEVT_CONNECT: {
            GEMINAI_LOGI(TAG, "AIOT_MQTTEVT_CONNECT");
        /*   TODO: 非常重要!!!
        *
        *   cur_version 要根据用户实际情况, 改成从设备的配置区获取, 要反映真实的版本号, 而不能像示例这样写为固定值
        *
        *   1. 如果设备从未上报过版本号, 在控制台网页将无法部署升级任务
        *   2. 如果设备升级完成后, 上报的不是新的版本号, 在控制台网页将会显示升级失败
        *
        */
        // 上报版本号
        report_version();
            /* TODO: 处理SDK建连成功, 不可以在这里调用耗时较长的阻塞函数 */
        }
        break;

        /* SDK因为网络状况被动断连后, 自动发起重连已成功 */
        case AIOT_MQTTEVT_RECONNECT: {
            GEMINAI_LOGI(TAG, "AIOT_MQTTEVT_RECONNECT");
            /* TODO: 处理SDK重连成功, 不可以在这里调用耗时较长的阻塞函数 */
        }
        break;

        /* SDK因为网络的状况而被动断开了连接, network是底层读写失败, heartbeat是没有按预期得到服务端心跳应答 */
        case AIOT_MQTTEVT_DISCONNECT: {
            char *cause = (event->data.disconnect == AIOT_MQTTDISCONNEVT_NETWORK_DISCONNECT) ? ("network disconnect") :
                          ("heartbeat disconnect");
            GEMINAI_LOGI(TAG, "AIOT_MQTTEVT_DISCONNECT %s", cause);
            /* TODO: 处理SDK被动断连, 不可以在这里调用耗时较长的阻塞函数 */
        }
        break;

        default: {

        }
    }
}
#include "core_mqtt.h"
/* MQTT默认消息处理回调, 当SDK从服务器收到MQTT消息时, 且无对应用户回调处理时被调用 */
void demo_mqtt_default_recv_handler(void *handle, const aiot_mqtt_recv_t *packet, void *userdata)
{
    core_mqtt_handle_t *mqtt_handle = (core_mqtt_handle_t *)handle;
    switch (packet->type) {
        case AIOT_MQTTRECV_HEARTBEAT_RESPONSE: {
            // GEMINAI_LOGI(TAG, "heartbeat response");
            /* TODO: 处理服务器对心跳的回应, 一般不处理 */
            int delay_ms = mqtt_handle->sysdep->core_sysdep_time() - mqtt_handle->heartbeat_params.last_send_time;
            GEMINAI_LOGI(TAG, "heartbeat response delay = %d", delay_ms);
        }
        break;

        case AIOT_MQTTRECV_SUB_ACK: {
            GEMINAI_LOGI(TAG, "suback, res: -0x%04lX, packet id: %d, max qos: %d\n",
                   -packet->data.sub_ack.res, packet->data.sub_ack.packet_id, packet->data.sub_ack.max_qos);
            /* TODO: 处理服务器对订阅请求的回应, 一般不处理 */
        }
        break;

        case AIOT_MQTTRECV_PUB: {
            GEMINAI_LOGI(TAG, "pub: %s, qos: %d, topic: %.*s\n", packet->data.pub.payload, packet->data.pub.qos, packet->data.pub.topic_len, packet->data.pub.topic);
            //printf("pub, payload: %.*s\n", packet->data.pub.payload_len, packet->data.pub.payload);
            /* TODO: 处理服务器下发的业务报文 */
        }
        break;

        case AIOT_MQTTRECV_PUB_ACK: {
            GEMINAI_LOGI(TAG, "puback, packet id: %d\n", packet->data.pub_ack.packet_id);
            /* TODO: 处理服务器对QoS1上报消息的回应, 一般不处理 */
        }
        break;

        default: {

        }
    }
}

/* 下载收包回调, 用户调用 aiot_download_recv() 后, SDK收到数据会进入这个函数, 把下载到的数据交给用户 */
/* TODO: 一般来说, 设备升级时, 会在这个回调中, 把下载到的数据写到Flash上 */
static esp_ota_handle_t ota_handle_test_config ;
const esp_partition_t *current_partition = NULL;
const esp_partition_t *next_partition = NULL;
#define MAX_OTAREC_CNT 5  //ota 最大重连次数

static bool ota_started = false;
// 标志位 OTA重新连接失败标志位 1（失败）  0（成功）
uint8_t ota_rec_fail_flag = 0; 

void check_ota_reconnect(int32_t percent)
{
    static int32_t progress=0;
    static uint8_t ota_update_fail_cnt = 0; 

    if(percent >= progress) {
        progress = percent;
        return;
    }else{
        ota_update_fail_cnt++;
        ota_started = false;
        esp_ota_end(ota_handle_test_config);
        GEMINAI_LOGI(TAG, "ota update fail count = %d", (int)ota_update_fail_cnt);
    }

    
    if (ota_update_fail_cnt >= MAX_OTAREC_CNT){
        
        // 启动定时器
        if (ota_reset_timer != NULL) 
            xTimerStart(ota_reset_timer, 0);
        
        aiot_mqtt_download_setopt(g_dl_handle, AIOT_MDOPT_RECV_HANDLE, NULL);
        aiot_mqtt_download_deinit(&g_dl_handle);
        g_dl_handle = NULL;
        ota_update_fail_cnt = 0;
        ota_rec_fail_flag = 1;
        GEMINAI_LOGE(TAG,"OTA Upgrade FailedPreparing to restart the device......") ;
    }
    else{
        ota_rec_fail_flag =  0;
    }
}

void user_download_recv_handler(void *handle, const aiot_mqtt_download_recv_t *packet, void *userdata)
{
    uint32_t data_buffer_len = 0;
    /* 目前只支持 packet->type 为 AIOT_DLRECV_HTTPBODY 的情况 */
    if (!packet || AIOT_MDRECV_DATA_RESP != packet->type) {
        return;
    }
    check_ota_reconnect(packet->data.data_resp.percent);
    
    // 初始化 OTA分区
    if (!ota_started) {
        current_partition = esp_ota_get_running_partition();
        next_partition = esp_ota_get_next_update_partition(current_partition);

        if (current_partition == NULL) {
            GEMINAI_LOGI(TAG, "Failed to get current_partition");
            return; // 处理错误
        }        

        if (next_partition == NULL) {
            GEMINAI_LOGI(TAG, "Failed to get next OTA partition");
            return; // 处理错误
        }


        esp_err_t err = esp_ota_begin(next_partition, OTA_SIZE_UNKNOWN, &ota_handle_test_config);
        if (err != ESP_OK) {
            GEMINAI_LOGI(TAG, "esp ota begin is error");
            return; // 处理错误
        }
        ota_started = true; // OTA 已开始
        GEMINAI_LOGI(TAG, "OTA beginning OK");
    }

    esp_err_t err = esp_ota_write(ota_handle_test_config, packet->data.data_resp.data, packet->data.data_resp.data_size);
    if (err != ESP_OK) {
        GEMINAI_LOGI(TAG, "esp ota write is error");
        esp_ota_end(ota_handle_test_config); // 结束当前 OTA 过程
        return;
    }    

    data_buffer_len = packet->data.data_resp.data_size;

    printf("download %03ld%% done, +%ld bytes\r\n", packet->data.data_resp.percent, data_buffer_len);
    vTaskDelay(200);
}


void ota_reset_timer_callback(TimerHandle_t xTimer) {
    ota_rec_fail_flag = 0;
    // 打印日志以确认重置
    GEMINAI_LOGW("OTA Reset", "Flags reset to zero.");
}

void geminai_ota_test()
{
    GEMINAI_LOGI(TAG,"test geminai ota begin");
    ota_reset_timer = xTimerCreate(
        "OTAResetTimer",                      // 定时器名称
        pdMS_TO_TICKS(OTA_WAIT_TIME),                // 计时周期（十分钟）
        pdFALSE,                               // 自动重启
        (void *) 0,                           // 定时器 ID
        ota_reset_timer_callback               // 回调函数
    );
    while (1)
    {   
        // GEMINAI_LOGI(TAG,"test begin the loop ");
        if(g_dl_handle != NULL) {
            int32_t res = aiot_mqtt_download_process(g_dl_handle);
        GEMINAI_LOGI(TAG,"res = %d",(int)res) ;

            if(STATE_MQTT_DOWNLOAD_SUCCESS == res) {
                /* 升级成功，这里重启并且上报新的版本号 */
                    // 看看升级是否完成
                esp_err_t err = esp_ota_end(ota_handle_test_config);
                if (err != ESP_OK) {
                    GEMINAI_LOGE(TAG, "esp_ota_end failed (%s)", esp_err_to_name(err)); 
                }  
                // 成功后切换到新分区
                err = esp_ota_set_boot_partition(next_partition);
                if (err != ESP_OK) {
                    GEMINAI_LOGE(TAG, "Failed to set boot partition (%s)", esp_err_to_name(err)); 
                }
            
                printf("mqtt download ota success \r\n");
                aiot_mqtt_download_deinit(&g_dl_handle);
                g_dl_handle = NULL; 
                vTaskDelay(1000);
                esp_restart(); 
                break;
            }
             else if(STATE_MQTT_DOWNLOAD_FAILED_RECVERROR == res
                      || STATE_MQTT_DOWNLOAD_FAILED_TIMEOUT == res
                      || STATE_MQTT_DOWNLOAD_FAILED_MISMATCH == res) 
            {
                GEMINAI_LOGI(TAG,"mqtt download ota failed \r\n");
//                aiot_mqtt_download_deinit(&g_dl_handle);
//                break;
            }
        }
        vTaskDelay(5000);        
    }
}

/* 用户通过 aiot_ota_setopt() 注册的OTA消息处理回调, 如果SDK收到了OTA相关的MQTT消息, 会自动识别, 调用这个回调函数 */
void user_ota_recv_handler(void *ota_handle, aiot_ota_recv_t *ota_msg, void *userdata)
{
    uint32_t request_size = 3 * 1024;
    switch (ota_msg->type) {
    case AIOT_OTARECV_FOTA: {
        if (ota_rec_fail_flag == 1)
            break;

        if (NULL == ota_msg->task_desc || ota_msg->task_desc->protocol_type != AIOT_OTA_PROTOCOL_MQTT) {
            break;
        }

        if(g_dl_handle != NULL) {
            aiot_mqtt_download_deinit(&g_dl_handle);
        }

        printf("OTA target firmware version: %s, size: %lu Bytes\r\n", ota_msg->task_desc->version,
               ota_msg->task_desc->size_total);
        void *md_handler = aiot_mqtt_download_init();
        aiot_mqtt_download_setopt(md_handler, AIOT_MDOPT_TASK_DESC, ota_msg->task_desc);
        /* 设置下载一包的大小，对于资源受限设备可以调整该值大小 */
        aiot_mqtt_download_setopt(md_handler, AIOT_MDOPT_DATA_REQUEST_SIZE, &request_size);

        /* 部分场景下，用户如果只需要下载文件的一部分，即下载指定range的文件，可以设置文件起始位置、终止位置。
         * 若设置range区间下载，单包报文的数据有CRC校验，但SDK将不进行完整文件MD5校验，
         * 默认下载全部文件，单包报文的数据有CRC校验，并且SDK会对整个文件进行md5校验 */
        // uint32_t range_start = 10, range_end = 50 * 1024 + 10;
        // aiot_mqtt_download_setopt(md_handler, AIOT_MDOPT_RANGE_START, &range_start);
        // aiot_mqtt_download_setopt(md_handler, AIOT_MDOPT_RANGE_END, &range_end);

        aiot_mqtt_download_setopt(md_handler, AIOT_MDOPT_RECV_HANDLE, user_download_recv_handler);
        modbus_task_stop();
        attribute_send_task_stop();
        
        xTaskCreate(geminai_ota_test, "geminai_ota_test", 4096, NULL, 5, NULL);
        g_dl_handle = md_handler;
    }
    break;
    default:
        break;
    }
}

/* 执行aiot_mqtt_process的任务, 包含心跳发送和QoS1消息重发 */
void mqtt_process_task(void *args)
{
    int32_t res = STATE_SUCCESS;

    while (g_mqtt_process_thread_running) {
        res = aiot_mqtt_process(args);
        if (res == STATE_USER_INPUT_EXEC_DISABLED) {
            break;
        }
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

/* 执行aiot_mqtt_recv的任务, 包含网络自动重连和从服务器收取MQTT消息 */
void mqtt_recv_task(void *args)
{
    int32_t res = STATE_SUCCESS;

    while (g_mqtt_recv_thread_running) {
        res = aiot_mqtt_recv(args);
        if (res < STATE_SUCCESS) {
            if (res == STATE_USER_INPUT_EXEC_DISABLED) {
                break;
            }
            vTaskDelay(pdMS_TO_TICKS(1000));
        }
    }
}

/*mqtt三元组连接*/
int linkkit_start(const char *s_url, const char *key, const char *name, char *d_secret)
{
    int32_t     res = STATE_SUCCESS;
    int8_t public_instance = 0; /* 用公共实例, 该参数要设置为1. 若用独享实例(企业实例), 要将该参数设置为0 */
    //char       *url = "iot-040a0lx1.mqtt.iothub.aliyuncs.com"; /* 阿里云平台上海站点的域名后缀 */
    char url[128]  = {0}; /* 阿里云平台企业实例的接入点 */
    char        host[256] = {0}; /* 用这个数组拼接设备连接的云平台站点全地址, 规则是 ${productKey}.iot-as-mqtt.cn-shanghai.aliyuncs.com */
    uint16_t    port = 443;      /* 无论设备是否使用TLS连接阿里云平台, 目的端口都是443 */
    aiot_sysdep_network_cred_t cred; /* 安全凭据结构体, 如果要用TLS, 这个结构体中配置CA证书等参数 */


    /*设备三元组*/
    char product_key[64]   = {0};
    char device_name[64]   = {0};
    char device_secret[64] = {0};

    memcpy(url, s_url, strlen(s_url));
    memcpy(product_key, key, strlen(key));
    memcpy(device_name, name, strlen(name));
    memcpy(device_secret, d_secret, strlen(d_secret));

    /* 配置SDK的底层依赖 */
    aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile);
    /* 配置SDK的日志输出 */
    aiot_state_set_logcb(demo_state_logcb);

    /* 创建SDK的安全凭据, 用于建立TLS连接 */
    memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
    cred.option = AIOT_SYSDEP_NETWORK_CRED_SVRCERT_CA;  /* 使用RSA证书校验MQTT服务端 */
    cred.max_tls_fragment = 16384; /* 最大的分片长度为16K, 其它可选值还有4K, 2K, 1K, 0.5K */
    cred.sni_enabled = 1;                               /* TLS建连时, 支持Server Name Indicator */
    cred.x509_server_cert = ali_ca_cert;                 /* 用来验证MQTT服务端的RSA根证书 */
    cred.x509_server_cert_len = strlen(ali_ca_cert);     /* 用来验证MQTT服务端的RSA根证书长度 */

    /* 创建1个MQTT客户端实例并内部初始化默认参数 */
    mqtt_handle = aiot_mqtt_init();
    if (mqtt_handle == NULL) {
        GEMINAI_LOGD(TAG, "aiot_mqtt_init failed");
        return -1;
    }


    /* TODO: 如果以下代码不被注释, 则例程会用TCP而不是TLS连接云平台 */
    /*
    {
        memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
        cred.option = AIOT_SYSDEP_NETWORK_CRED_NONE;
    }
    */

    //snprintf(host, 100, "%s.%s", product_key, url);
    if (1 == public_instance)
    {
        snprintf(host, 256, "%s.%s", product_key, url);
    }
    else
    {
        snprintf(host, 256, "%s", url);
    }

    /* 配置MQTT服务器地址 */
    aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_HOST, (void *)host);
    /* 配置MQTT服务器端口 */
    aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PORT, (void *)&port);
    /* 配置设备productKey */
    aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PRODUCT_KEY, (void *)product_key);
    /* 配置设备deviceName */
    aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_NAME, (void *)device_name);
    /* 配置设备deviceSecret */
    aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_SECRET, (void *)device_secret);
    /* 配置网络连接的安全凭据, 上面已经创建好了 */
    aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_NETWORK_CRED, (void *)&cred);
    /* 配置MQTT默认消息接收回调函数 */
    aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_RECV_HANDLER, (void *)demo_mqtt_default_recv_handler);
    /* 配置MQTT事件回调函数 */
    aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_EVENT_HANDLER, (void *)demo_mqtt_event_handler);

    /* 与MQTT例程不同的是, 这里需要增加创建OTA会话实例的语句 */
    ota_handle = aiot_ota_init();
    if (NULL == ota_handle) {
        return -1;
    }

    /* 用以下语句, 把OTA会话和MQTT会话关联起来 */
    aiot_ota_setopt(ota_handle, AIOT_OTAOPT_MQTT_HANDLE, mqtt_handle);
    /* 用以下语句, 设置OTA会话的数据接收回调, SDK收到OTA相关推送时, 会进入这个回调函数 */
    aiot_ota_setopt(ota_handle, AIOT_OTAOPT_RECV_HANDLER, user_ota_recv_handler);


    /* 与服务器建立MQTT连接 */
    res = aiot_mqtt_connect(mqtt_handle);
    if (res < STATE_SUCCESS) {
        /* 尝试建立连接失败, 销毁MQTT实例, 回收资源 */
        aiot_mqtt_deinit(&mqtt_handle);
        GEMINAI_LOGD(TAG, "aiot_mqtt_connect failed: -0x%04lX\n", -res);
        return -1;
    }

    /*创建rtos任务*/
    g_mqtt_process_thread_running = 1;
    xTaskCreatePinnedToCore(mqtt_process_task, "mqtt_process", 4096, (void*)mqtt_handle, 3, NULL, tskNO_AFFINITY);
    g_mqtt_recv_thread_running = 1;
    xTaskCreatePinnedToCore(mqtt_recv_task, "mqtt_recv", 4096, (void*)mqtt_handle, 3, NULL, tskNO_AFFINITY);   

    return 0;

    /* MQTT 订阅topic功能示例, 请根据自己的业务需求进行使用 */
    /*
    if(0)
    {
        char sub_topic[128] = {0};
        sprintf(sub_topic, "/%s/%s/user/get", key, name);        

        res = aiot_mqtt_sub(mqtt_handle, sub_topic, NULL, 1, NULL);
        if (res < 0) {
            GEMINAI_LOGD(TAG, "aiot_mqtt_sub failed, res: -0x%04lX\n", -res);
            return -1;
        }
    }*/

    /* MQTT 发布消息功能示例, 请根据自己的业务需求进行使用 */
    /*if(0)
    {
        char *pub_topic = "/sys/a13FN5TplKq/mqtt_basic_demo/thing/event/property/post";
        char *pub_payload = "{\"id\":\"1\",\"version\":\"1.0\",\"params\":{\"LightSwitch\":0}}";

        res = aiot_mqtt_pub(mqtt_handle, pub_topic, (uint8_t *)pub_payload, strlen(pub_payload), 0);
        if (res < 0) {
            GEMINAI_LOGD(TAG, "aiot_mqtt_pub failed, res: -0x%04lX\n", -res);
            return -1;
        }
    }

    return 0;*/
}

/*断开连接*/
int linkkit_stop(void)
{
    int32_t     res = STATE_SUCCESS;
    /* 断开MQTT连接 */
    res = aiot_mqtt_disconnect(mqtt_handle);
    if (res < STATE_SUCCESS) {
        aiot_mqtt_deinit(&mqtt_handle);
        GEMINAI_LOGD(TAG, "aiot_mqtt_disconnect failed: -0x%04lX\n", -res);
        return -1;
    }

    /* 销毁MQTT实例 */
    res = aiot_mqtt_deinit(&mqtt_handle);
    if (res < STATE_SUCCESS) {
        GEMINAI_LOGD(TAG, "aiot_mqtt_deinit failed: -0x%04lX\n", -res);
        return -1;
    }

    g_mqtt_process_thread_running = 0;
    g_mqtt_recv_thread_running = 0;

    return 0;
}
#include "geminai_vision_1106.h"
void task_connect_aliyun(void *args)
{
    char *url_ds      = "https://public-api-cny-test.keynection.cn/api/iot-service/v1/device/device-info";
    char url[48]      = {0};
    char secret[] = "device-cny-pepsi";
    /* TODO: 替换为自己设备的三元组 */
    // char product_key[24]       = {0};
    char *device_name       = base_station.imei;  
    char device_secret[64] = {0};
    http_rest_with_url(url_ds, secret, device_name,device_secret, base_station.asset_id, url, base_station.product_key);
    // char* device_secret = "6420c5b29acdbeed785debd451d1ee01";
    int retryCnt = 0,ret=0;
    while(1)
    {   
        retryCnt++;
        ret = linkkit_start(url, base_station.product_key, device_name, device_secret);
        if( ret== 0)
        {
            mqtt_quere_init();
            if(_24V_INT_Read() > 19){
                boot_event_send();      
                send_location_info();
                attribute_send_task_start();
            }
            break;
        }
        else

        if(retryCnt > 3)
            break;
    }

    vTaskDelete(NULL);
}

void mqtt_task_start(void)
{
    static bool start= false;
    if(start == false){
         xTaskCreate(task_connect_aliyun, "task_connect_aliyun", 1024*5, NULL, 5, NULL);
    }
    start = true;
}

